#include "Enemy.h"
#include "ConfigManager.h"

Enemy::Enemy()
{
    timerSkill.SetOneShot(false);
    timerSkill.SetOnTimeout([&]() { onSkillReleased(this); });

    timerSketch.SetOneShot(true);
    timerSketch.SetWaitTime(0.075);
    timerSketch.SetOnTimeout([&]() { isShowSketch = false; });

    timerRestoreSpeed.SetOneShot(true);
    timerRestoreSpeed.SetOnTimeout([&]() { speed = maxSpeed; });
}

void Enemy::OnUpdate(double delta)
{
    timerSkill.OnUpdate(delta);
    timerSketch.OnUpdate(delta);
    timerRestoreSpeed.OnUpdate(delta);

    Vector2 moveDistance = velocity * delta;
    Vector2 targetDistance = positionTarget - position;
    position += (moveDistance < targetDistance ? moveDistance : targetDistance);

    if (targetDistance.ApproxZero()) {
        idxTarget++;
        RefreshPositionTarget();
        direction = (positionTarget - position).Normalize();
    }

    velocity.x = direction.x * speed * SIZE_TILE;
    velocity.y = direction.y * speed * SIZE_TILE;

    bool is_show_x_amin = abs(velocity.x) >= abs(velocity.y);

    if (isShowSketch) {
        if (is_show_x_amin)
            animCurrent = velocity.x > 0 ? &anim_right_sketch : &anim_left_sketch;
        else
            animCurrent = velocity.y > 0 ? &anim_down_sketch : &anim_up_sketch;
    } else {
        if (is_show_x_amin)
            animCurrent = velocity.x > 0 ? &anim_right : &anim_left;
        else
            animCurrent = velocity.y > 0 ? &anim_down : &anim_up;
    }

    animCurrent->OnUpdate(delta);
}

void Enemy::OnRender(SDL_Renderer *renderer)
{
    static SDL_Rect rect;
    static SDL_Point point;
    static const int offset_y = 2;
    static const Vector2 size_hp_bar = {40, 8};
    static const SDL_Color color_border = {116, 185, 124, 255};
    static const SDL_Color color_content = {226, 255, 194, 255};

    // 中心点位置转到左上角位置
    point.x = (int) (position.x - size.x / 2);
    point.y = (int) (position.y - size.y / 2);

    animCurrent->OnRender(renderer, point);

    if (hp < maxHp) {
        rect.x = (int) (position.x - size_hp_bar.x / 2);
        rect.y = (int) (position.y - size.y / 2 - size_hp_bar.y - offset_y);
        rect.w = (int) (size_hp_bar.x * (hp / maxHp));
        rect.h = (int) size_hp_bar.y;
        SDL_SetRenderDrawColor(renderer, color_content.r, color_content.g, color_content.b, color_content.a);
        SDL_RenderFillRect(renderer, &rect);

        rect.w = (int) size_hp_bar.x;
        SDL_SetRenderDrawColor(renderer, color_border.r, color_border.g, color_border.b, color_border.a);
        SDL_RenderDrawRect(renderer, &rect);
    }
}

void Enemy::SetOnSkillReleased(Enemy::SkillCallback func)
{
    this->onSkillReleased = std::move(func);
}

void Enemy::IncreaseHp(double val)
{
    hp += val;
    if (hp > maxHp) {
        hp = maxHp;
    }
}

void Enemy::DecreaseHp(double val)
{
    hp -= val;
    if (hp <= 0) {
        hp = 0;
        isValid = false;
    }
    isShowSketch = true;
    timerSketch.Restart();
}

void Enemy::SlowDown()
{
    speed = maxSpeed - 0.5;
    timerRestoreSpeed.SetWaitTime(1);
    timerRestoreSpeed.Restart();
}

void Enemy::SetPosition(const Vector2 &position)
{
    this->position = position;
}

void Enemy::SetRouter(const Router *rout)
{
    this->router = rout;
    RefreshPositionTarget();
}

void Enemy::MakeInvalid()
{
    isValid = false;
}

double Enemy::GetHp() const
{
    return hp;
}

const Vector2 &Enemy::GetSize() const
{
    return size;
}

const Vector2 &Enemy::GetPosition() const
{
    return position;
}

const Vector2 &Enemy::GetVelocity() const
{
    return velocity;
}

double Enemy::GetDamage() const
{
    return damage;
}

double Enemy::GetRewardRatio() const
{
    return rewardRatio;
}

double Enemy::GetRecoverRadius() const
{
    return SIZE_TILE * recoverRange;
}

double Enemy::GetRecoverIntensity() const
{
    return recoverIntensity;
}

bool Enemy::CanRemove() const
{
    return !isValid;
}

double Enemy::GetRouterProcess() const
{
    if (router->GetIdxList().size() == 1) { return 1; }
    return (double) idxTarget / (router->GetIdxList().size() - 1);
}

void Enemy::RefreshPositionTarget()
{
    const Router::IdxList &idxList = router->GetIdxList();
    if (idxTarget < idxList.size()) {
        const SDL_Point &point = idxList[idxTarget];
        static const SDL_Rect &rectTileMap = ConfigManager::Instance().rectTileMap;

        positionTarget.x = rectTileMap.x + point.x * SIZE_TILE + SIZE_TILE / 2;
        positionTarget.y = rectTileMap.y + point.y * SIZE_TILE + SIZE_TILE / 2;
    }
}

void Enemy::SetEnemyAttribute(ResID resId, ResID sketchResId)
{
    static ResourcesManager::TexturePool &texturePool = ResourcesManager::Instance().texturePool;
    static SDL_Texture *tex = texturePool[resId];
    static SDL_Texture *texSketch = texturePool[sketchResId];
    static ConfigManager::EnemyTemplate &slim_template = ConfigManager::Instance().slim_template;

    static const std::vector<int> idx_list_up = {6, 7, 8, 9, 10, 11};
    static const std::vector<int> idx_list_down = {0, 1, 2, 3, 4, 5};
    static const std::vector<int> idx_list_left = {18, 19, 20, 21, 22, 23};
    static const std::vector<int> idx_list_right = {12, 13, 14, 15, 16, 17};

    anim_up.SetLoop(true);
    anim_up.SetInterval(0.1);
    anim_up.SetFrameData(tex, 6, 4, idx_list_up);
    anim_down.SetLoop(true);
    anim_down.SetInterval(0.1);
    anim_down.SetFrameData(tex, 6, 4, idx_list_down);
    anim_left.SetLoop(true);
    anim_left.SetInterval(0.1);
    anim_left.SetFrameData(tex, 6, 4, idx_list_left);
    anim_right.SetLoop(true);
    anim_right.SetInterval(0.1);
    anim_right.SetFrameData(tex, 6, 4, idx_list_right);

    anim_up_sketch.SetLoop(true);
    anim_up_sketch.SetInterval(0.1);
    anim_up_sketch.SetFrameData(texSketch, 6, 4, idx_list_up);
    anim_down_sketch.SetLoop(true);
    anim_down_sketch.SetInterval(0.1);
    anim_down_sketch.SetFrameData(texSketch, 6, 4, idx_list_down);
    anim_left_sketch.SetLoop(true);
    anim_left_sketch.SetInterval(0.1);
    anim_left_sketch.SetFrameData(texSketch, 6, 4, idx_list_left);
    anim_right_sketch.SetLoop(true);
    anim_right_sketch.SetInterval(0.1);
    anim_right_sketch.SetFrameData(texSketch, 6, 4, idx_list_right);

    maxHp = slim_template.hp;
    maxSpeed = slim_template.speed;
    damage = slim_template.damage;
    rewardRatio = slim_template.reward_ratio;
    recoverInterval = slim_template.recover_interval;
    recoverRange = slim_template.recover_range;
    recoverIntensity = slim_template.recover_intensity;

    size.x = 48, size.y = 48;
    hp = maxHp, speed = maxSpeed;
}